﻿#region Using

using System;
using System.Collections.Generic;
using System.Linq;

using Common.Base;
using Common.HexagonBoard;
using Common.HexagonBoard.Enums;

#endregion

namespace GameLogic.Internal
{

    #region Using

    #endregion

    internal class FarmersBreakfastHexagonBoard : HexagonBoard<Tile>
    {
        /// <summary>
        /// Number of players participating.
        /// </summary>
        private readonly uint playerCount;

        /// <summary>
        /// Responsible for calculating the starting positions.
        /// </summary>
        private PositionCalculatorDelegate positionCalculator;

        internal FarmersBreakfastHexagonBoard(Size dimension, uint playerCount)
            : base(dimension, ChooseTopology(playerCount))
        {
            if (playerCount > 6)
            {
                throw new NotImplementedException();
            }

            this.playerCount = playerCount;
        }

        internal FarmersBreakfastHexagonBoard(Size dimension, StructurizerDelegate structurizer, uint playerCount)
            : base(dimension, structurizer)
        {
            if (playerCount > 6)
            {
                throw new NotImplementedException();
            }

            this.playerCount = playerCount;
        }

        /// <summary>
        /// Gets all tiles for a specific player.
        /// </summary>
        /// <param name="owner">The Owner of the requested tiles.</param>
        /// <returns></returns>
        internal IEnumerable<Tile> GetPlayerTiles(Player owner)
        {
            return this.Where(x => x.Owner.Equals(owner));
        }

        /// <summary>
        /// Gets the tiles functioning as starting positions of the board.
        /// </summary>
        /// <returns>All tiles picked as starting positions for the players.</returns>
        internal IEnumerable<Tile> GetStartingTiles()
        {
            switch (this.Topology)
            {
                case Topology.Hexagon:
                    this.positionCalculator = this.CalculatePositionHexagon;
                    break;

                case Topology.Triangle:
                    this.positionCalculator = this.CalculatePositionTriangle;
                    break;

                case Topology.Default:
                    break;

                case Topology.Customized:
                    break;
            }

            return this.positionCalculator();
        }

        /// <summary>
        /// Calculates the starting positions of a conventional hexagon board
        /// based on the number of players.
        /// </summary>
        /// <returns>The tiles, which were picked to be the starting positions.</returns>
        private IEnumerable<Tile> CalculatePositionHexagon()
        {
            var possiblePositions = new Position[6];

            var center = new Position();
            center.X = this.Dimension.Width / 2;
            center.Y = (this.Dimension.Height / 2) + (center.X / 2);

            // hexagon edge length
            var edge = this.Dimension.Width / 2;

            // all corner positions clock-wise starting from north
            possiblePositions[0] = new Position(center.X, center.Y + edge);
            possiblePositions[1] = new Position(center.X + edge, center.Y + edge);
            possiblePositions[2] = new Position(center.X + edge, center.Y);
            possiblePositions[3] = new Position(center.X, center.Y - edge);
            possiblePositions[4] = new Position(center.X - edge, center.Y - edge);
            possiblePositions[5] = new Position(center.X - edge, center.Y);

            // all starting positions
            var positions = new List<Tile>();

            var currentIndex = new Random().Next(6);

            positions.Add(this.GetTile(possiblePositions[currentIndex]));

            // starting with second player position as the first is already selected
            for (var i = 1; i < this.playerCount; i++)
            {
                int nextIndex;

                if (i % 2 == 0)
                {
                    // right neighbor
                    nextIndex = (currentIndex + 1) % 6;
                    positions.Add(this.GetTile(possiblePositions[nextIndex]));
                }
                else
                {
                    // opposition
                    nextIndex = (currentIndex + 3) % 6;
                    positions.Add(this.GetTile(possiblePositions[nextIndex]));
                }

                currentIndex = nextIndex;
            }

            return positions;
        }

        /// <summary>
        /// Calculates the starting positions for a conventional triangle board for 
        /// exactly three players.
        /// </summary>
        /// <returns>The tiles, which were picked to be the starting positions.</returns>
        private IEnumerable<Tile> CalculatePositionTriangle()
        {
            var positions = new List<Tile>();

            positions.Add(this.GetTile(new Position(0, 0)));
            positions.Add(this.GetTile(new Position(0, this.Dimension.Height)));
            positions.Add(this.GetTile(new Position(this.Dimension.Width, this.Dimension.Height)));

            return positions;
        }

        /// <summary>
        /// Decides on the topology based on the number of needed bases.
        /// </summary>
        /// <param name="baseCount">Number of headquarters on the map.</param>
        /// <returns>The chosen topology.</returns>
        private static Topology ChooseTopology(uint baseCount)
        {
            if (baseCount == 3)
            {
                return Topology.Triangle;
            }

            if (baseCount > 6)
            {
                return Topology.Default;
            }

            return Topology.Hexagon;
        }

        internal delegate IEnumerable<Tile> PositionCalculatorDelegate();
    }
}